package cn.seaboot.admin.mvc.cors;

import cn.seaboot.commons.core.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 跨域配置
 *
 * <p>
 * 普通的配置方式：客户端与系统的 url，是 n:1 的关系。
 * 调整后的跨域配置：客户端与系统的 url，是 1:n 的关系。
 *
 * @author Mr.css
 * @version 2020-07-08 09:04
 * @see CorsAntConfigProvider 当前配置类，也是个跨域信息提供器
 **/
@Configuration
public class DefaultCorsAntConfigProvider implements CorsAntConfigProvider {
    private final Logger logger = LoggerFactory.getLogger(DefaultCorsAntConfigProvider.class);

    @Resource
    private CorsProperties corsProperties;

    /**
     * 暂存了跨域配置信息
     */
    private final Map<String, CorsAntConfig> corsConfigurations = new LinkedHashMap<>();

    /**
     * 这里并没有按照动态查询的方式实现，而是一开始读取了全部的配置，存储在了Map中。
     */
    @PostConstruct
    public void init() {
        logger.info("[--- Configuration ---]Cross domain setting: start...");
        List<CorsProperties> crossDomainList = corsProperties.getConfig();
        if (CommonUtils.isNotEmpty(crossDomainList)) {
            for (CorsProperties cross : crossDomainList) {
                CorsConfiguration corsConfiguration = new CorsConfiguration();
                String origin = cross.getAllowedOrigin();
                if (origin.endsWith("/") || origin.endsWith("\\")) {
                    origin = origin.substring(0, origin.length() - 1);
                }
                corsConfiguration.addAllowedOrigin(origin);

                // AllowedHeader
                String allowHeader = cross.getAllowedHeader();
                if (CommonUtils.isNotEmpty(allowHeader)) {
                    for (String header : allowHeader.split(",")) {
                        corsConfiguration.addAllowedHeader(header);
                    }
                }

                // AllowedMethod
                String allowedMethod = cross.getAllowedMethod();
                if (CommonUtils.isNotEmpty(allowedMethod)) {
                    for (String method : allowedMethod.split(",")) {
                        corsConfiguration.addAllowedMethod(method);
                    }
                }
                // AllowedHeader
                String exposedHeader = cross.getExposedHeader();
                if (CommonUtils.isNotEmpty(exposedHeader)) {
                    for (String header : exposedHeader.split(",")) {
                        corsConfiguration.addExposedHeader(header);
                    }
                }

                // MaxAge
                if (cross.getMaxAge() != null) {
                    corsConfiguration.setMaxAge(cross.getMaxAge().longValue());
                }

                // AllowCredentials
                if (cross.getAllowCredentials() != null) {
                    corsConfiguration.setAllowCredentials(cross.getAllowCredentials());
                }
                logger.info("Cross domain using config: {}", cross);
                CorsAntConfig config = new CorsAntConfig(cross.getAntPath(), corsConfiguration);

                // 注意 key 值，这是我们与默认配置不同的地方
                corsConfigurations.put(cross.getAllowedOrigin(), config);
            }
        } else {
            logger.info("Cross domain without any configuration!");
        }
        logger.info("[--- Configuration ---]Cross domain setting: finish");
    }

    /**
     * 根据客户端配置信息，查找对应的跨域配置
     *
     * @param origin 客户端的 url
     * @return 配置信息
     */
    @Override
    public CorsAntConfig findCorsAntConfig(String origin) {
        return this.corsConfigurations.get(origin);
    }

    /**
     * 构建一个跨域拦截器
     *
     * @return 跨域拦截器
     */
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        DynamicCorsConfiguration configuration = new DynamicCorsConfiguration();
        configuration.setCrossAntConfigProvider(this);
        CorsFilter filter = new CorsFilter(configuration);
        FilterRegistrationBean<CorsFilter> filterRegistrationBean = new FilterRegistrationBean<>(filter);
        filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return filterRegistrationBean;
    }
}
